using System;
using System.Collections;
using System.IO;

using System.Windows.Media.DirectShow.Ogg;

namespace System.Windows.Media.DirectShow.Vorbis
{

    public class VorbisStream
    {

        private ILogicalOggStream oggStream;
        private IdentificationHeader identificationHeader;
        private CommentHeader commentHeader;
        private SetupHeader setupHeader;

        private AudioPacket lastAudioPacket;
        private int[][] currentPcm;
        private int currentPcmIndex;
        private int currentPcmLimit;

        private const int IDENTIFICATION_HEADER = 1;
        private const int COMMENT_HEADER = 3;
        private const int SETUP_HEADER = 5;

        //private int bitIndex=0;
        //private byte lastByte=(byte)0;
        //private bool initialized=false;

        //private Object streamLock=new Object();
        //private int[][] pcmBuffer;
        private int pageCounter = 0;

        public VorbisStream(ILogicalOggStream oggStream)
        {
            this.oggStream = oggStream;

            for (int i = 0; i < 3; i++)
            {
                ByteArrayBitStream babs = new ByteArrayBitStream(oggStream.GetNextOggPacket());
                int headerType = babs.ReadInt32(8);

                switch (headerType)
                {
                    case IDENTIFICATION_HEADER:
                        //System.Console.WriteLine("id header");
                        identificationHeader = new IdentificationHeader(this, babs);
                        break;
                    case COMMENT_HEADER:
                        //System.Console.WriteLine("comment header");
                        commentHeader = new CommentHeader(this, babs);
                        break;
                    case SETUP_HEADER:
                        //System.Console.WriteLine("setup header");
                        setupHeader = new SetupHeader(this, babs);
                        break;
                }
            }

            if (identificationHeader == null)
            {
                throw new VorbisFormatException("The file has no identification header.");
            }

            if (commentHeader == null)
            {
                throw new VorbisFormatException("The file has no comment header.");
            }

            if (setupHeader == null)
            {
                throw new VorbisFormatException("The file has no setup header.");
            }

            currentPcm = new int[Channels][];
            for (int i = 0; i < Channels; i++)
            {
                currentPcm[i] = new int[16384];
            }
        }

        public IdentificationHeader IdentificationHeader
        {
            get
            {
                return identificationHeader;
            }
            set
            {
            }
        }

        public CommentHeader CommentHeader
        {
            get
            {
                return commentHeader;
            }
            set
            {
            }
        }

        public SetupHeader SetupHeader
        {
            get
            {
                return setupHeader;
            }
            set
            {
            }
        }

        public bool Open
        {
            get
            {
                return oggStream.Open;
            }
            set
            {
                if (!value)
                {
                    oggStream.Close();
                }
            }
        }

        public int ReadPcm(byte[] sampleBuffer)
        {

            int bufferIndex = 0;

            int channels = Channels;

            if (lastAudioPacket == null)
            {
                lastAudioPacket = GetNextAudioPacket();
            }
            if (currentPcm == null || currentPcmIndex >= currentPcmLimit)
            {
                AudioPacket ap = GetNextAudioPacket();
                if (ap != null)
                    currentPcmLimit = ap.GetPcm(lastAudioPacket, currentPcm);
                //System.out.println("currentPcmLimit: "+currentPcmLimit);
                currentPcmIndex = 0;
                lastAudioPacket = ap;

                if (ap == null)
                    return 0;
            }
            int written = 0;
            int i = 0;
            for (i = currentPcmIndex; i < currentPcmLimit; i++)
            {
                if (sampleBuffer.Length - bufferIndex > 2 * currentPcmLimit)
                {
                    for (int j = 0; j < channels; j++)
                    {
                        int s = currentPcm[j][i];
                        sampleBuffer[bufferIndex++] = (byte)(s & 0xff);
                        sampleBuffer[bufferIndex++] = (byte)((s >> 8) & 0xff);
                        written += 2;
                    }
                }
                else
                {
                    break;
                }
            }
            currentPcmIndex = i;
            return written;
        }

        /*
       public void setNextPage(int pageIndex) {
         try {
            oggStream.setNextPage(pageIndex);
            //bitPipe.byteAllign();
            lastAudioPacket=null;
         }
         catch(Exception e) {
            e.printStackTrace();
         }
       }
        */

        public AudioPacket GetNextAudioPacket()
        {
            pageCounter++;
            byte[] source = oggStream.GetNextOggPacket();
            if (source != null)
            {
                AudioPacket res = new AudioPacket(this, new ByteArrayBitStream(source));
                return res;
            }
            else
                return null;
        }

        public int SampleRate
        {
            get
            {
                return identificationHeader.SampleRate;
            }
        }

        public int Channels
        {
            get
            {
                return identificationHeader.Channels;
            }
        }

        public void Reset()
        {
            pageCounter = 0;
            currentPcmIndex = 0;
            lastAudioPacket = null;
        }
    }

}